/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = wtc_header.c                                      */
/*  NOTE      =                                                    */
/*  DATE      = 2015/08/13 by songbb                               */
/*******************************************************************/

#include <stdio.h>
#include <netinet/in.h>
#include <frct_comm.h>

#include "wtc_comm.h"
#include "wtc_core.h"
#include "frzlib.h"


/*******************************************************************************
*  FUNC     :  检测通用包头
*           :  *p_payload                           (数据内容)
*           :  ui_payload_len                       (数据长度)
*           :  **pp_inflated_payload                (如果有压缩,这是解压后的数据内容)
*           :  *p_ui_inflated_payload_len           (如果有压缩,这是解压后的数据长度)
*  RTN      :  APM_L7_IS_YES                        (是WTC协议)
*              APM_L7_IS_UNKNOWN                    (不能判断该协议)
*  NOTE     : 需要外部释放pp_inflated_payload的内存
*******************************************************************************/
int wtc_header_detect(char *p_payload
                    , unsigned int ui_payload_len
                    , char **pp_inflated_payload
                    , unsigned int *p_ui_inflated_payload_len)
{
    unsigned int *p_ui_magic;
    unsigned int ui_magic = 0;
    unsigned int *p_ui_flags;
    unsigned int ui_flags = 0;
    char *p_inflated = NULL;
    unsigned long ul_inflated_len = 0L;
    char *p_inflated_full = NULL; // 加上32个字节包头之后的解压后的数据

    if (ui_payload_len < APM_WTC_PKT_HEADER_LEN)
        goto unknown;

    p_ui_magic = (unsigned int *) p_payload;
    ui_magic = (unsigned int) ntohl(*p_ui_magic);
    if (ui_magic != APM_WTC_HEADER_SMAGIC)
    {
        if (ui_payload_len < APM_WTC_PKT_HEADER_LEN + 4)
            goto unknown;

        p_ui_flags = (unsigned int *) (p_payload + 20);
        ui_flags = (unsigned int) ntohl(*p_ui_flags);
        if ((ui_flags & 0x100) != 0) // 被压缩的,需要先解压
        {
            WTC_PRINT_WARN("detect deflated data...\n");
            if (frzlib_inflater((Byte *) (p_payload + APM_WTC_PKT_HEADER_LEN), ui_payload_len - APM_WTC_PKT_HEADER_LEN, &p_inflated, &ul_inflated_len) != FR_SUCC)
            {
                WTC_PRINT_WARN("inflate failed\n");
                goto unknown;
            }
//            PRINT_HEX_STRING("inflate ok", p_inflated, (unsigned int) ul_inflated_len);

            p_ui_magic = (unsigned int *) p_inflated;
            ui_magic = (unsigned int) ntohl(*p_ui_magic);
            if (ui_magic != APM_WTC_HEADER_SMAGIC && ui_magic != APM_WTC_HEADER_MAGIC)
            {
                if (ui_payload_len < APM_WTC_PKT_HEADER_LEN + 20)
                {
                    wtc_free((void **) &p_inflated);
                    goto unknown;
                }

                p_ui_magic = (unsigned int *) (p_inflated + 16);
                ui_magic = (unsigned int) ntohl(*p_ui_magic);
                if (ui_magic != APM_WTC_HEADER_SMAGIC && ui_magic != APM_WTC_HEADER_MAGIC)
                {
                    wtc_free((void **) &p_inflated);
                    goto unknown;
                }
            }

            *p_ui_inflated_payload_len = (unsigned int) (APM_WTC_PKT_HEADER_LEN + ul_inflated_len);
            if ((p_inflated_full = (char *) frmalloc(*p_ui_inflated_payload_len)) == NULL)
            {
                WTC_MALLOCK_ERR;
                wtc_free((void **) &p_inflated);
                *p_ui_inflated_payload_len = 0;
                goto unknown;
            }
            memset(p_inflated_full, 0x00, *p_ui_inflated_payload_len);

            // 先加入32个字节的报文头
            memcpy(p_inflated_full, p_payload, APM_WTC_PKT_HEADER_LEN);
            // 再加入解压后的报文体
            memcpy(p_inflated_full + APM_WTC_PKT_HEADER_LEN, p_inflated, ul_inflated_len);

            *pp_inflated_payload = p_inflated_full;

            wtc_free((void **) &p_inflated);
        }
        else
        {
            p_ui_magic = (unsigned int *) (p_payload + APM_WTC_PKT_HEADER_LEN);
            ui_magic = (unsigned int) ntohl(*p_ui_magic);
            if (ui_magic != APM_WTC_HEADER_SMAGIC && ui_magic != APM_WTC_HEADER_MAGIC)
            {
                if (ui_payload_len < APM_WTC_PKT_HEADER_LEN + 20)
                    goto unknown;

                p_ui_magic = (unsigned int *) (p_payload + APM_WTC_PKT_HEADER_LEN + 16);
                ui_magic = (unsigned int) ntohl(*p_ui_magic);
                if (ui_magic != APM_WTC_HEADER_SMAGIC && ui_magic != APM_WTC_HEADER_MAGIC)
                    goto unknown;
            }
        }
    }

    return APM_L7_IS_YES;
unknown:
    return APM_L7_IS_UNKNOWN;
}

/*******************************************************************************
*  FUNC     :  解析通用包头
*  ARGS     :  *p_payload                           (数据内容)
*           :  ui_payload_len                       (数据长度)
*           :  *p_st_wtc_header                     (通用头结构体)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int wtc_header_unpack(char *p_payload
                    , unsigned int ui_payload_len
                    , WTC_header *p_st_wtc_header)
{
    unsigned int *p_ui_magic;
    unsigned int ui_magic;
    unsigned int *p_ui_size;
    unsigned int *p_ui_uncmprs_sz;
    unsigned int *p_ui_flags;

    if (ui_payload_len < APM_WTC_PKT_HEADER_LEN)
        goto error;

    p_ui_magic = (unsigned int *) p_payload;
    ui_magic = (unsigned int) ntohl(*p_ui_magic);
    if (ui_magic == APM_WTC_HEADER_SMAGIC)
    {
        p_st_wtc_header->em_tfmh_type = APM_WTC_TFMH_TYPE_DOM;

        p_ui_flags = (unsigned int *) (p_payload + 24);
        p_st_wtc_header->ui_flags = (unsigned int) ntohl(*p_ui_flags);

        p_ui_size = (unsigned int *) (p_payload + 28);
        p_st_wtc_header->ui_size = (unsigned int) ntohl(*p_ui_size);
    }
    else
    {
        if (ui_payload_len < APM_WTC_PKT_HEADER_LEN + 4)
            goto error;

        p_ui_magic = (unsigned int *) (p_payload + APM_WTC_PKT_HEADER_LEN);
        ui_magic = (unsigned int) ntohl(*p_ui_magic);
        if (ui_magic == APM_WTC_HEADER_SMAGIC || ui_magic == APM_WTC_HEADER_MAGIC)
        {
            p_st_wtc_header->em_tfmh_type = APM_WTC_TFMH_TYPE_ORIG;

            p_ui_flags = (unsigned int *) (p_payload + 20);
            p_st_wtc_header->ui_flags = (unsigned int) ntohl(*p_ui_flags);

            p_ui_uncmprs_sz = (unsigned int *) (p_payload + 24);
            p_st_wtc_header->ui_uncmprs_sz = (unsigned int) ntohl(*p_ui_uncmprs_sz);

            if ((p_st_wtc_header->ui_flags & 0x100) != 0)
                p_st_wtc_header->ui_size = p_st_wtc_header->ui_uncmprs_sz;
            else
            {
                p_ui_size = (unsigned int *) (p_payload + 16);
                p_st_wtc_header->ui_size = (unsigned int) ntohl(*p_ui_size);
            }
        }
        else
        {
            if (ui_payload_len < APM_WTC_PKT_HEADER_LEN + 20)
                goto error;

            p_ui_magic = (unsigned int *) (p_payload + APM_WTC_PKT_HEADER_LEN + 16);
            ui_magic = (unsigned int) ntohl(*p_ui_magic);
            if (ui_magic == APM_WTC_HEADER_SMAGIC || ui_magic == APM_WTC_HEADER_MAGIC)
            {
                p_st_wtc_header->em_tfmh_type = APM_WTC_TFMH_TYPE_WS;

                p_ui_flags = (unsigned int *) (p_payload + 24);
                p_st_wtc_header->ui_flags = (unsigned int) ntohl(*p_ui_flags);

                p_ui_uncmprs_sz = (unsigned int *) (p_payload + 28);
                p_st_wtc_header->ui_uncmprs_sz = (unsigned int) ntohl(*p_ui_uncmprs_sz);

                if ((p_st_wtc_header->ui_flags & 0x100) != 0)
                    p_st_wtc_header->ui_size = p_st_wtc_header->ui_uncmprs_sz;
                else
                {
                    p_ui_size = (unsigned int *) (p_payload + 20);
                    p_st_wtc_header->ui_size = (unsigned int) ntohl(*p_ui_size);
                }
            }
            else
                goto error;
        }
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}
